<?php

/**
 * @Author: king
 * @Date:   2019-05-25 21:13:24
 * @Last Modified by:   king
 * @Last Modified time: 2019-05-25 21:52:46
 */

namespace App\Tools;
use Elasticsearch\ClientBuilder;

/**
 * Class Elasticsearch
 * @package App\Tools
 */
class Elasticsearch
{
    /**
     * 连接参数配置
     * @var array
     */
    private $options = [
        'username' => 'elastic',
        'password' => 'Lanmei!Test1',
        'host' => 'es-cn-459151l1n000uo1zb.public.elasticsearch.aliyuncs.com',
        'port' => '9200'
    ];
    /**
     * Elasticsearch Client
     * @var \Elasticsearch\Client|null
     */
    protected static $handler = null;
    /**
     * 当前静态实例
     * @var Elasticsearch|null
     */
    protected static $instance = null;
    /**
     * 查询使用的所有参数
     * @var array
     */
    protected $param = [
        'size' => 10,
        'from' => 0,
    ];

    private $page = 1;

    /**
     * 设置连接用户名
     * @param $param
     * @return $this
     */
    public function setUsername($param)
    {
        $this->options['username'] = $param;
        return $this;
    }

    /**
     * 设置连接密码
     * @param $param
     * @return $this
     */
    public function setPassword($param)
    {
        $this->options['password'] = $param;
        return $this;
    }

    /**
     * 设置连接host地址
     * @param $param
     * @return $this
     */
    public function setHost($param)
    {
        $this->options['host'] = $param;
        return $this;
    }

    /**
     * 设置连接端口
     * @param $param
     * @return $this
     */
    public function setPort($param)
    {
        $this->options['port'] = $param;
        return $this;
    }

    /**
     * 构造方法
     * Elasticsearch constructor.
     * @param array $options
     */
    private function __construct($options = [])
    {
        if (!empty($options))
        {
            $this->options = array_merge($this->options, $options);
        }
    }

    /**
     * 获取当前类静态实例
     * @return Elasticsearch|null
     */
    public static function getInstance()
    {
        if (is_null(self::$instance))
        {
            // 从配置文件中获取参数，并传递
            $config = config('elastic');

            self::$instance = new self($config);
        }
        return self::$instance;
    }

    /**
     * 获取Elasticsearch Client
     * @return \Elasticsearch\Client|null
     */
    private function getClient()
    {
        if (is_null(self::$handler))
        {
            $host = $this->options['username'] . ':' . $this->options['password'] . '@' . $this->options['host'] . ':' . $this->options['port'];

            self::$handler = ClientBuilder::create()->setHosts([$host])->build();
        }
        return self::$handler;
    }

    /**
     * 设置需要在哪个索引中查询
     * @param string $param counselor
     * @return $this
     */
    public function setParamIndex($param)
    {
        $this->param['index'] = $param;
        return $this;
    }

    /**
     * 设置查询结果最大返回条目
     * @param int $param
     * @return $this
     */
    public function setParamSize($param = 10)
    {
        $this->param['size'] = $param;
        return $this;
    }

    /**
     * 设置当前查询页码
     * @param int $page
     * @return $this
     */
    public function setParamPage($page = 1)
    {
        $this->page = (int)$page;
        $this->param['from'] = ($this->page - 1) * $this->param['size'];
        return $this;
    }

    /**
     * 设置需要返回的字段
     * @param array|string $param
     * @return $this
     */
    public function setParamFields($param)
    {
        if (is_string($param))
        {
            $param = explode(',', $param);
        }
        if (is_array($param))
        {
            $this->param['body']['_source'] = $param;
        }
        return $this;
    }

    /**
     * 设置查询排序条件
     * 支持多字段排序
     * @param array $param
     * [
     *  'uid' => ['order' => 'desc'],
     *  'id' => ['order' => 'asc'],
     *  'stargazer_level' => ['order' => 'desc'],
     * ]
     * @return $this
     */
    public function setParamSort($param)
    {
        $this->param['body']['sort'] = $param;

        return $this;
    }

    /**
     * 设置查询过滤条件
     * @param array $param
     * [
     *  'match' => ['nickname' => "小"]
     * ]
     * @return $this
     */
    public function setParamWhere($param = [])
    {
        if (empty($param))
        {
            $this->param['body']['query'] = ["match_all" => new \stdClass()];
        } else
        {
            $this->param['body']['query'] = $param;
        }
        return $this;
    }


    /**
     * 查询
     * Elasticsearch::getInstance()
     *  ->setParamFields($field)
     *  ->setParamIndex($index)
     *  ->setParamSize($pageSize)
     *  ->setParamPage($page)
     *  ->setParamSort($sort)
     *  ->setParamWhere($map)
     *  ->search();
     * @return array
     */
    public function search()
    {
        // 初始化分页数据格式
        $data = [
            "total" => 0,
            "per_page" => $this->param['size'],
            "current_page" => $this->page,
            "last_page" => 1,
            'data' => []
        ];
        $client = $this->getClient();

        $response = $client->search($this->param);

        if (isset($response['hits']['total']) && $response['hits']['total'] > 0)
        {
            $data['total'] = $response['hits']['total'];

            $data['last_page'] = (int)ceil($data['total'] / $this->param['size']) ?? 1;

            $data['data'] = array_column($response['hits']['hits'], '_source');

            return $data;
        }

        return $data;
    }
}
